package se.webbzon.oschi01.tools.mapper;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

import se.webbzon.oschi01.gui.MainFrame;
import se.webbzon.oschi01.gui.MinecraftIconSheet;
import se.webbzon.oschi01.gui.ModelUtils;
import se.webbzon.oschi01.gui.SortedListModel;
import se.webbzon.oschi01.item.ItemEntry;
import se.webbzon.oschi01.item.ItemEntryComparator;
import se.webbzon.oschi01.item.TranslationJsonReader;
import se.webbzon.oschi01.item.TranslationJsonWriter;
import se.webbzon.oschi01.singleton.Singletons;
import se.webbzon.oschi01.utils.EntryComparator;
import se.webbzon.oschi01.utils.Progress;
import se.webbzon.oschi01.utils.ProgressEvent;

public class MapperFrame extends JDialog {
	
	private final MapperModel model;
	
	private final MapperSearcher searcher;
	
	private final MapperAutomapper automapper;
	
	private final MapperPanel mappingPanel;
	
	private final JButton automapButton;
	
	private final JProgressBar progressBar;
	
	private final JList<Entry<ItemEntry,ItemEntry>> mappingList;
	
	public MapperFrame(MapperModel model) {
		super(Singletons.instance(MainFrame.class));
		this.model = model;
		searcher = new MapperSearcher();
		automapper = new MapperAutomapper();
		model.addObserver(new ObserverImpl());
		
		setIconImage(Singletons.instance(MinecraftIconSheet.class).getIcon(new Point(12,3)).getImage());
		setTitle("Mapper Tool");
		getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));
		
		// Add mapping panel
		mappingPanel = new MapperPanel(model,searcher);
		add(mappingPanel);
		
		// Add center panel
		JPanel centerPanel = new JPanel();
		centerPanel.setLayout(new BoxLayout(centerPanel,BoxLayout.X_AXIS));
		centerPanel.add(new JButton(new AddAction()));
		centerPanel.add(Box.createHorizontalStrut(4));
		automapButton = new JButton(new AutoAddAction());
		centerPanel.add(automapButton);
		centerPanel.add(Box.createHorizontalStrut(4));
		centerPanel.add(new JLabel("Progress: "));
		progressBar = new JProgressBar();
		centerPanel.add(progressBar);
		centerPanel.setMinimumSize(new Dimension(0,32));
		centerPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE,32));
		add(centerPanel);
		
		// Add mapping list
		EntryComparator<ItemEntry,ItemEntry> cmp;
		cmp = EntryComparator.createKeyComparator(new ItemEntryComparator());
		mappingList = new JList<Entry<ItemEntry,ItemEntry>>(ModelUtils.toSortedListModel(ModelUtils.toListModel(model.getMappings()),cmp));
		add(new JScrollPane(mappingList,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
		
		// Add bottom panel
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout(bottomPanel,BoxLayout.X_AXIS));
		bottomPanel.add(new JButton(new RemoveAction()));
		bottomPanel.add(Box.createHorizontalStrut(4));
		bottomPanel.add(new JButton(new RemoveAllAction()));
		bottomPanel.add(Box.createHorizontalStrut(4));
		bottomPanel.add(new JButton(new SaveAction()));
		bottomPanel.add(Box.createHorizontalStrut(4));
		bottomPanel.add(new JButton(new LoadAction()));
		add(bottomPanel);
		
		setPreferredSize(new Dimension(800,600));
		pack();
		setVisible(true);
	}
	
	public void updateMappingList(Entry<ItemEntry,ItemEntry> listItem) {
		SortedListModel<Entry<ItemEntry,ItemEntry>> lm;
		lm = (SortedListModel<Entry<ItemEntry,ItemEntry>>) mappingList.getModel();
		boolean inModel = model.isMapped(listItem.getKey());
		boolean inList = lm.contains(listItem);
		if (inModel && !inList)
			lm.add(listItem);
		else if (!inModel && inList)
			lm.remove(listItem);
	}
	
	public void updateMappingList() {
		EntryComparator<ItemEntry,ItemEntry> cmp;
		cmp = EntryComparator.createKeyComparator(new ItemEntryComparator());
		mappingList.setModel(ModelUtils.toSortedListModel(model.getMappings(),cmp));
	}
	
	private final class ObserverImpl implements Observer {

		@Override public void update(Observable o, Object e) {
			MapperEvent event = (MapperEvent) e;
			if (event.getType() == MapperEvent.Action.MAPPING_CHANGED) {
				mappingPanel.updateFromList();
				updateMappingList();
			} else {
				mappingPanel.updateFromList(event.getArgument().getKey());
				updateMappingList(event.getArgument());
			}
		}
		
	}
	
	private final class AutoAddAction extends AbstractAction implements Observer {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 6826733114628068925L;

		public AutoAddAction() {
			putValue(NAME, "Automap");
		}

		@Override public void actionPerformed(ActionEvent e) {
			automapper.setMetric(searcher.getMetric());
			automapper.setSearchLimit(searcher.getSearchLimit());
			automapper.setTolerance(searcher.getTolerance());
			Progress<?> p = automapper.automap(model);
			p.addObserver(this);
			MapperFrame.this.setEnabled(false);
			
		}

		@SuppressWarnings("unchecked")
		@Override public void update(Observable o, Object arg) {
			if (arg instanceof ProgressEvent) {
				ProgressEvent e = (ProgressEvent) arg;
				switch (e.getType()) {
				case COMPLETED:
					Map<ItemEntry,ItemEntry> result;
					result = (Map<ItemEntry,ItemEntry>) e.getArgument();
					model.mapItems(result);
					JOptionPane.showMessageDialog(MapperFrame.this, result.size() + " items were automapped!");
				case ABORTED:
					progressBar.setValue(0);
					o.deleteObserver(this);
					MapperFrame.this.setEnabled(true);
					break;
				default:
					progressBar.setValue((int) e.getArgument());
				}
			}
		}
		
	}
	
	private final class SaveAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = 8290310957999983293L;

		public SaveAction() {
			putValue(NAME, "Save");
		}

		@Override public void actionPerformed(ActionEvent e) {
			JFileChooser fc = new JFileChooser();
			int val = fc.showSaveDialog(MapperFrame.this);
			if (val == JFileChooser.APPROVE_OPTION) try {
				File file = fc.getSelectedFile();
				if (!file.exists())
					file.createNewFile();
				TranslationJsonWriter writer = new TranslationJsonWriter();
				try (FileOutputStream fos = new FileOutputStream(file)) {
					writer.write(model.getMappings(), fos);
				}
				JOptionPane.showMessageDialog(MapperFrame.this, "Mapping sucessfully saved!");
			} catch (IOException ex) {
				JOptionPane.showMessageDialog(MapperFrame.this, ex.getMessage());
			}
		}
		
	}
	
	private final class LoadAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1905905672952139801L;

		public LoadAction() {
			putValue(NAME, "Load");
		}

		@Override public void actionPerformed(ActionEvent e) {
			JFileChooser fc = new JFileChooser();
			int val = fc.showOpenDialog(MapperFrame.this);
			if (val == JFileChooser.APPROVE_OPTION) try {
				File file = fc.getSelectedFile();
				if (file.exists()) {
					TranslationJsonReader reader = new TranslationJsonReader();
					try (FileInputStream fis = new FileInputStream(file)) {
						Map<ItemEntry,ItemEntry> map = reader.read(fis);
						model.mapItems(map);
					}
				}
				JOptionPane.showMessageDialog(MapperFrame.this, "Mapping sucessfully loaded!");
			} catch (IOException ex) {
				ex.printStackTrace();
				JOptionPane.showMessageDialog(MapperFrame.this, ex.getMessage());
			}
		}
		
	}
	
	private final class AddAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = 7334988982804457505L;

		public AddAction() {
			putValue(NAME, "Add");
		}
		
		@Override public void actionPerformed(ActionEvent arg0) {
			Entry<ItemEntry,ItemEntry> mapping = mappingPanel.getMapping();
			if (mapping != null) {
				model.mapItem(mapping.getKey(), mapping.getValue());
			}
		}
		
	}
	
	private final class RemoveAllAction extends AbstractAction {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = -4517118665638280514L;

		public RemoveAllAction() {
			putValue(NAME, "Remove all");
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			model.clearMappings();
		}
		
	}
	
	private final class RemoveAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = -5852313075667638786L;

		public RemoveAction() {
			putValue(NAME, "Remove");
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			Collection<Entry<ItemEntry,ItemEntry>> selected = mappingList.getSelectedValuesList();
			ArrayList<ItemEntry> rm = new ArrayList<ItemEntry>();
			for (Entry<ItemEntry,ItemEntry> entry : selected)
				rm.add(entry.getKey());
			model.removeMappings(rm);
		}
		
	}

}
